(function($) {
	var params = {};
	
	// --- General --- {{
	
	/**
	 * @param object obj
	 * @return int
	 */
	function objectPropertyCount(obj) {
		var count = 0;
		for (prop in obj) {
			count++;
		}
		return count;
	}
	
	/**
	 * @param mixed needle
	 * @param array haystack
	 * @return bool
	 */
	function inArray(needle, haystack) {
		if (typeof(haystack) == "object") {
			for (var i=0; i < haystack.length; i++) {
				if (needle == haystack[i]) {
					return true;
				}
				
			}
		}		
		return false;
	}
	
	// --- General --- }}
	
	// --- Params --- {{
	
	/**
	 * @return void
	 */
	function setDefaultParams() {
		params.pager = null;
		params.url = null;
		params.rowList = [10,20,50];
		params.rowNum = 10;
		params.colNames = null;
		params.colModel = null;
		params.sortname = 'id';
		params.sord = 'desc';
		params.primaryIndex = 'id';
		params.page = 1;
		params.sidx = null;
		params.paginator = {};
		params.simpleSearchClass = 'simple-search';
		params.emptyList = 'Empty list';
		params.actionsWidth = '8%';
		params.urlLink = null;
	} 

	/**
	 * @param string columnName
	 * @return array
	 */
	function getSidx(columnName) {
		return (null != params.colModel[columnName]['sort']) ? 
			params.colModel[columnName]['sort'] : params.colModel[columnName]['index'];
	}
	
	/**
	 * @param object settings
	 * @return void
	 */
	function setParams(settings) {
		setDefaultParams();
		if (settings) {
			$.extend(params, settings);
		}
		params.sord = params.sord.toLocaleLowerCase(); 
		
		params.sidx = getSidx(params.sortname); 
	}
	
	/**
	 * @return void
	 */
	function inverseSord() {
		params.sord = (params.sord == 'asc') ? 'desc' : 'asc';
	}
	
	/**
	 * @param object data
	 * @return void
	 */
	function paginatorInit(data) {
		params.paginator.first = data.first; //Номер первой страницы (т.е. 1)
		params.paginator.firstItemNumber = data.firstItemNumber; //Действительный номер первого элемента на текущей странице
		params.paginator.firstPageInRange = data.firstPageInRange; //Первая страница в ряде, возвращенном текущим стилем прокрутки
		params.paginator.current = data.current; //Текущий номер страницы
		params.paginator.currentItemCount = data.currentItemCount; //Количество элементов на текущей странице
		params.paginator.itemCountPerPage = data.itemCountPerPage; //Максимальное количество отображаемых на странице элементов
		params.paginator.last = data.last; //Номер последней страницы
		params.paginator.lastItemNumber = data.lastItemNumber; //Действительный номер последнего элемента на текущей странице
		params.paginator.lastPageInRange = data.lastPageInRange; //Последняя страница в ряде, возвращенном текущим стилем прокрутки
		params.paginator.next = data.next; //Номер следующей страницы
		params.paginator.pageCount = data.pageCount; //Общее количество страниц
		params.paginator.pagesInRange = data.pagesInRange; //Массив страниц, возвращенный текущим стилем прокрутки
		params.paginator.previous = data.previous; //Номер предыдущей страницы
		params.paginator.totalItemCount = data.totalItemCount; //Общее количество элементов
	}
	
	// --- Params --- }}
	
	// --- View --- {{
	
	/**
	 * @return void
	 */
	function initViewHtml() {
		var dialog_delete_html = '<div id="dialog-delete" title="Подтверждение"><span>Удалить?</span></div>';
		var dialog_error_html = '<div id="dialog-error" title="Ошибка"><span style="color: red;">Не удается выполнить операцию.</span></div>';
		var processing_html = '<div class="ui-widget-overlay" style="display: none; width: 100%; height: 100%; z-index: 1001;"></div>' 
							+ '<img style="display: none; z-index: 1002;" class="processing" src="/css/jqueryui/cupertino/images/ui-anim_basic_16x16.gif" />';
							
		var html = dialog_delete_html + dialog_error_html + processing_html; 
		$('body').append(html);
		
		$("#dialog-delete").dialog({
			modal: true,
			autoOpen: false,
			buttons: {
				Ok: function() {
					$(this).dialog('close');
					rowDeleteConfirmed();
				}
			} 
		});
		$("#dialog-error").dialog({ 
			autoOpen: false,
			buttons: {
				Ok: function() {
					$(this).dialog('close');
				}
			} 
		});
	}
	
	/**
	 * @param array data
	 * @param string selected
	 * @return string 
	 */
	function getSelectboxOptions(data, selected) {
		var view = '';
		var selectedHtml;
		if (typeof(data) == 'object') {
			for(var i=0; i < data.length; i++){
				selectedHtml = (selected == data[i]) ? 'selected="selected"' : '';
				view += '<option ' + selectedHtml + 'value="' + data[i] + '">' + data[i] + '&nbsp;</option>';
				
			}	
		}
		return view;
	} 
	
	/**
	 * Class
	 */
	function Arrow()
	{
		this.up = '<span class="ui-icon ui-icon-triangle-1-n pager-arrow pager-arrow-up"></span>';
		this.down = '<span class="ui-icon ui-icon-triangle-1-s pager-arrow pager-arrow-down"></span>';
		this.no = '<span class="ui-icon ui-icon-triangle-2-e-w pager-arrow pager-arrow-no"></span>';
		
		/**
		 * @return string
		 */
		this.getCurrent = function() {
			if (params.sord == 'desc') {
				return this.up;	
			}
			if (params.sord == 'asc') {
				return this.down;	
			}
		}
		
	}
	var aArrow = new Arrow();
	
	/**
	 * @param object data
	 * @return string view
	 */
	function getDataView(data) {
		var row = data.row;
		var view;
		var name, i, j;
		var index;
		var columnWidth;

		var currentArrow;
		
		if (data.totalItemCount == 0) {
			return params.emptyList;
		}
		
		view = '<table class="admin-pager">';
		/* --- navigation --- {{ */ 
		view += '<tr class="navigation-pager">';
		view += '<td class="pager-navigation" colspan="' 
			  + (objectPropertyCount(params.colNames)+1) 
			  + '">'
			 
			  + '<div style="float: left;">View ' + params.paginator.firstItemNumber + '-' + params.paginator.lastItemNumber + ' of ' + params.paginator.totalItemCount + '</div>' 
			  
			  + '<div style="float: right;" class="pager-navigation-controll">'

			  + '<select onchange="jQuery(\'' + params.pager + '\').mainPagerMethod(\'paginator_item_count_per_page\',jQuery(this).val())">' 
			  + getSelectboxOptions(params.rowList, params.paginator.itemCountPerPage) 
			  + '</select>&nbsp;&nbsp;&nbsp;&nbsp;'
			  
			  + '<a href="javascript: void jQuery(\'' + params.pager + '\').mainPagerMethod(\'paginator_first\')"><<</a></span>&nbsp;&nbsp;'
			  + '<a href="javascript: void jQuery(\'' + params.pager + '\').mainPagerMethod(\'paginator_previous\')"><</a></span>&nbsp;&nbsp;'
			  
			  + 'Page '
			  + '<input onkeyup="jQuery(\'' + params.pager + '\').mainPagerMethod(\'paginator_current\', jQuery(this).val(), event);" value="' + params.page + '"  autocomplete="off" />'
			  + ' of ' + params.paginator.pageCount + '&nbsp;&nbsp;'
			  
			  + '<a href="javascript: void jQuery(\'' + params.pager + '\').mainPagerMethod(\'paginator_next\')">></a>&nbsp;&nbsp;'
			  + '<a href="javascript: void jQuery(\'' + params.pager + '\').mainPagerMethod(\'paginator_last\')">>></a>'
			  
			  + '</div>'
			  
			  + '</td>';
		view += '</tr>';
		/* --- navigation --- }} */
		
		
		/* --- headers --- {{ */
		view += '<tr class="head-pager">';
		for (name in params.colNames) {
			currentArrow = aArrow.no;
			if ( params.sidx == getSidx(name) ) {
				currentArrow = aArrow.getCurrent();
			}
			columnWidth = (params.colModel[name]['width'] != null) ? 'width="' + params.colModel[name]['width'] + '"' : '';
			view += '<td id="column_' + name + '"' + columnWidth + '><a href="javascript: void jQuery(\'' + params.pager + '\').mainPagerMethod(\'sort_column\', \'' + name + '\')">' + '<span style="float:left;">' + params.colNames[name] + '</span>'
			     + currentArrow                                                                                                                                                            		
                 + '</a></td>';
		}
		view += '<td width="' + params.actionsWidth + '"></td>';
		view += '</tr>';
		/* --- headers --- }} */
		
		/* --- data rows --- {{ */
		var urlLinkHtmlOnmouseover = '';
		var urlLinkHtmlOnclick = '';
		if (params.urlLink != null) {
			urlLinkHtmlOnmouseover = 'onmouseover="this.style.cursor=\'pointer\'" ';
			urlLinkHtmlOnclick = ' onclick="window.location=\''+ params.urlLink +'\'" ';
		}
		
		for (i in row) {
			view += '<tr class="general-row" onmouseover="jQuery(this).addClass(\'pager-row-active\');" onmouseout="jQuery(this).removeClass(\'pager-row-active\')">';
			urlLinkHtmlOnclick = ' onclick="window.location=\''+ params.urlLink +'/'+params.primaryIndex+'/'+row[i][params.primaryIndex]+'/\'" ';
			for (name in params.colModel) {
				view += '<td '+urlLinkHtmlOnclick+urlLinkHtmlOnmouseover + ' >';
				for (j in params.colModel[name]['index']) {
					index = params.colModel[name]['index'][j];
					view += row[i][index];
				}				
				view += '</td>';
			}
			view += '<td class="pager_actions">';
				view += '<span class="ui-state-default"><a href="javascript: void jQuery(\'' + params.pager + '\').mainPagerMethod(\'row_edit\',' + row[i][params.primaryIndex] + ');" class="ui-icon ui-icon-pencil admin-pager-edit"></a></span>';
				view += '<span class="ui-state-default"><a href="javascript: void jQuery(\'' + params.pager + '\').mainPagerMethod(\'row_delete\',' + row[i][params.primaryIndex] + ');" class="ui-icon ui-icon-trash admin-pager-delete"></a></span>';
			view += '</td>';	
				
			view += '</tr>';
		}
		/* --- data rows --- }} */
		view += '</table>';
		
		return view;
	}
	
	// --- View --- }}
	
	// --- Ajax --- {{
	
	/**
	 * @param object ajaxParams
	 * @return void
	 */
	function load(ajaxParams) {
		var timer = null;
		var rows = (null != params.paginator.itemCountPerPage) ? 
				params.paginator.itemCountPerPage : params.rowNum;
		
		var loadParams = {
				"ac": "load", 
				"page": params.page,
				"rows": rows,
				"sidx": params.sidx,
				"sord": params.sord	
		}; 
		if (null != ajaxParams) {
			$.extend(loadParams, ajaxParams);
		}

		timer = setTimeout(function(){
			$(".ui-widget-overlay").show();
			$(".processing").show();
		}, 1000);
		
		$.post(params.url, loadParams, function(data){
			if (timer != null) {
				clearTimeout(timer);
				$(".ui-widget-overlay").hide();
				$(".processing").hide();
			} 
			
			if (data == false ) {
				$("#dialog-error").dialog('open');
			} else {
				params.page = data.current;
				
				paginatorInit(data);
				var view = getDataView(data);
				$('.loading-message').hide();
				
				$(params.pager).html(view);
				$(params.pager).show();
			}	
		});
	}
	
	/**
	 * @param int value
	 * @return void 
	 */
	function paginatorItemCountPerPage(value) {
		params.paginator.itemCountPerPage = value;
		load({
			"ac": "set_item_count_per_page", 
			"value": value
		});
	}
	
	/**
	 * @return void
	 */
	function rowDeleteConfirmed() {
		load({
			"ac": "delete_row", 
			"primary": rowDelete.primary
		});
	}
	
	/**
	 * @param string primary
	 * @return void
	 */
	function rowDelete(primary) {
		$("#" . this.param.).dialog('open');
		rowDelete.primary = primary;
	}
	
	/**
	 * @param int page
	 * @return void
	 */
	function paginatorCurrent(page) {
		params.page = page;
		load({
			"ac": "set_current", 
			"page": page
		});
	}
	
	/**
	 * @param string colName
	 */
	function sortColumn(colName) {
		if (!$('.head-pager td#column_' + colName + ' a span.pager-arrow').hasClass('pager-arrow-no') ) {
			inverseSord();
		}
		
		params.sortname = colName;
		params.sidx = getSidx(colName);
		
		$('span.pager-arrow').remove();
		$('tr.head-pager td a').append(aArrow.no);
		$('.head-pager td#column_' + colName + ' a span.pager-arrow').remove();
		var currentArrow = aArrow.getCurrent();
		$('.head-pager td#column_' + colName + ' a').append(currentArrow);
			
		load();
	}
	
	/**
	 * @return void
	 */
	function simpleSearch() {
		var timer = null;
		var searchString = null;
		var searchStringPrevious = null;
		$('.' + params.simpleSearchClass).keyup(function(){
			searchString = $(this).val();
			if (searchString == searchStringPrevious) { 
				return;
			}
			searchStringPrevious = searchString; 
			if (timer != null) {
				clearTimeout(timer);
				$(".ui-widget-overlay").hide();
				$(".processing").hide();
			} 
			timer = setTimeout(function(){
				$(".ui-widget-overlay").show();
				$(".processing").show();
				load({
					'ac' : 'simple_search',
					'search_string' : searchString  
				});
			}, 1000);
			  
		});
		
	}
	
	// --- Ajax --- }}
	
	// --- Public --- {{
	
	/**
	 * @param object setting
	 * @return object
	 */
	$.fn.mainPager = function(settings) {
		
		console.log(params);
		
		setParams(settings);
		initViewHtml();
		$('.loading-message').show();
		load();
		simpleSearch();
		return this;
	};
	
	/**
	 * @param string name
	 * @return object
	 */
	$.fn.mainPagerMethod = function(name) {
		if (name == 'row_edit') {
			 var urlEdit = params.urlEdit + '/' + params.primaryIndex + '/' + arguments[1];
			 window.location = urlEdit;
		} 
		
		if (name == 'row_delete') {
			rowDelete(arguments[1]);
		}	
		
		if (name == 'paginator_current') {
			var eventArg = arguments[2];
			if (eventArg.keyCode == 13) {
				paginatorCurrent(arguments[1]);
			}
			eventArg.preventDefault();
		}
		
		if (inArray(name, ['paginator_first', 'paginator_previous', 'paginator_next', 'paginator_last'])) {
			var nameParts = name.split('_');
			var paginatorProp = nameParts[1];
			
			paginatorCurrent(params.paginator[paginatorProp]);
		}
		
		if (name == 'paginator_item_count_per_page') {
			paginatorItemCountPerPage(arguments[1]);
		}
		
		if (name == 'sort_column') {
			sortColumn(arguments[1]);
		}
		

		return this;
	};
	
	// --- Public --- }}
	
})(jQuery);